/* 树链剖分 思想
* 1.关键 树中任意一条路径->O(logn)段连续区间
    (1)将树转化为一段区间
    (2)将树上的任意一条路径转化为 O(logn) 段区间

* 2.转换方法
    重儿子: 节点个数最多的儿子，且每个有儿子的节点 有且仅有一个重儿子
    重边：连接该节点与它的重儿子的边
    重链：由一系列重边相连得到的链
    轻链：由一系列非重边相连得到的链
    先深搜出重儿子，然后按 dfs 序优先遍历重儿子的原则给节点重新编号(保证重链上的所有编号是连续的)，进而求出一段连续区间

* 3.使用
    对子树进行修改和查询，由于 dfs 遍历方式的原因，子树中的编号都是连续的，相当于对一段区间进行修改和查询
    对路径进行修改和查询，类似于 LCA 的思想，每次提取最低的重链构成的区间，然后对这些区间进行修改和查询
*/

#define DEBUG
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+10, M=N<<1; //树是双向的
using ll=long long;
#define l(u) (u<<1)
#define r(u) (u<<1|1)
#define L(u) tree[u<<1]
#define R(u) tree[u<<1|1]

inline int read()
{
	int x=0;char c=getchar();
	while (c<'0'||c>'9') c=getchar();
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48,c=getchar();
	return x;
}
inline void write(ll x)
{
	if (x>=10) write(x/10);
	putchar(x%10+48);
}//快读快输

struct Node
{
    int l, r;
    ll add, sum; //懒标记 区间和
}tree[N<<2];
int n, m, root, MOD;
int w[N], h[N], e[M], ne[M], idx;
int id[N], nw[N], cnt; //dfs序 dfs序对应的权值 dfs序的计数器(节点编号)
int dep[N], sz[N], top[N], fa[N], son[N]; //节点深度 当前节点的字数大小 节点所在重链的顶端节点 

inline void printSeg()
{
    for(int i=1; tree[i].l!=0; i++)
                printf("test:[%d, %d, %lld]\n", tree[i].l, tree[i].r, tree[i].sum);
}
inline void AddEdge(int a, int b)
{
    e[idx]=b, ne[idx]=h[a], h[a]=idx++;
}

void dfs1(int u, int father, int depth) //求每个点的深度 父节点 子树大小 儿子节点
{
    dep[u]=depth, fa[u]=father, sz[u]=1;
    for(int i=h[u]; ~i; i=ne[i])
    {
        int v=e[i];
        if(v==father) continue;
        dfs1(v, u, depth+1);
        sz[u]+=sz[v];
        if(sz[son[u]]<sz[v]) son[u]=v; //找到重儿子
    }
}

void dfs2(int u, int t) //求dfs序 当前节点 当前重链的顶部节点 
{
    id[u]=++cnt, nw[cnt]=w[u], top[u]=t;
    if(!son[u]) return; //没有重儿子->叶子节点
    dfs2(son[u], t); //按照重链深搜
    for(int i=h[u]; ~i; i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue; //去重 在重链上
        dfs2(v, v);//非重链的深搜
    }
}

inline void pushup(int u) //更新当前节点的sum
{
    tree[u].sum=(L(u).sum+R(u).sum+MOD)%MOD;
}

void pushdown(int u)
{
    auto &U=tree[u], &L=L(u), &R=R(u);
    if(U.add)
    {
        L.add=(L.add+U.add+MOD)%MOD, L.sum=(L.sum+U.add*(L.r-L.l+1)%MOD+MOD)%MOD;
        R.add=(R.add+U.add+MOD)%MOD, R.sum=(R.sum+U.add*(R.r-R.l+1)%MOD+MOD)%MOD;
        U.add=0;
    }
}

void build(int u, int l, int r)
{
    tree[u]={l, r, 0, nw[r]};
    if(l==r) return;
    int mid=l+r>>1;
    build(l(u), l, mid), build(r(u), mid+1, r);
    pushup(u);
}

void modify(int u, int l, int r, int k) //区间修改长度
{
    auto &U=tree[u];
    if(l<=U.l && U.r<=r)
    {
        U.add=(U.add+k+MOD)%MOD;
        U.sum=(U.sum+k*(U.r-U.l+1)+MOD)%MOD;
        return;
    }
    pushdown(u);
    int mid=U.l+U.r>>1;
    if(l<=mid) modify(l(u), l, r, k);
    if(mid<r)  modify(r(u), l, r, k);
    pushup(u);
}

ll query(int u, int l, int r) //查找区间和
{
    auto &U=tree[u];
    if(l<=U.l && U.r<=r) return U.sum;
    pushdown(u);
    int mid=U.l+U.r>>1;
    ll res=0;
    if(l<=mid) res=(res+query(l(u), l, r)+MOD)%MOD;
    if(mid<r)  res=(res+query(r(u), l, r)+MOD)%MOD;
    return res;
}

void updatePath(int u, int v, int k) //利用dfs序更新路径u->v上的值
{
    while(top[u]!=top[v]) //u需要向上走至u,v在同一条重链上
    {
        if(dep[top[u]]<dep[top[v]]) swap(u, v);
        modify(1, id[top[u]], id[u], k);
        u=fa[top[u]]; 
    }
    if(dep[u]<dep[v]) swap(u, v);
    modify(1, id[v], id[u], k);
}

ll queryPath(int u, int v)
{
    int res=0;
    while(top[u]!=top[v]) //u需要向上走至u,v在同一条重链上
    {
        if(dep[top[u]]<dep[top[v]]) swap(u, v);
        res=(res+query(1, id[top[u]], id[u])+MOD)%MOD;
        u=fa[top[u]]; 
    }
    if(dep[u]<dep[v]) swap(u, v); //u在v之上
    res=(res+query(1, id[v], id[u])+MOD)%MOD;
    return res;
}

void updateTree(int u, int k) //利用dfn在线段树区间上更新节点u和其子树的节点值
{
    modify(1, id[u], id[u]+sz[u]-1, k);
}

ll queryTree(int u)
{
    return query(1, id[u], id[u]+sz[u]-1)%MOD;
}

void solve()
{
    memset(h, -1, sizeof h);

    n=read(); m=read(); root=read(), MOD=read();
    for(int i=1; i<=n; i++) w[i]=read();

    for(int i=1; i<=n-1; i++)
    {
        int a=read(), b=read();
        AddEdge(a, b); AddEdge(b, a); 
    }

    dfs1(root, -1, 1);
    dfs2(root, root);
    build(1, 1, n);

    
    while(m--)
    {
        int op=read(), u=read(), v, k;
        if(op==1)
        {
            v=read(), k=read();
            updatePath(u, v, k);
        }
        else if(op==3)
        {
            k=read();
            updateTree(u, k);
        }
        else if(op==2)
        {
            v=read();
            write(queryPath(u, v)); puts("");
        }
        else if(op==4)
        {
            write(queryTree(u)); puts("");
        }
    }
}

signed main()
{
    #ifdef DEBUG
        freopen("./in.txt","r",stdin);
        freopen("./out.txt","w",stdout);
    #endif
    int T=1;
    // T=read();
    while(T--)
        solve();

    return 0;
}
